package com.zyx.javademo.juc.scheduled;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


/**
 * @author Yaxi.Zhang
 * @since 2021/6/10 09:27
 * desc: ScheduledExecutorService实例
 * 自JDK1.5开始，JDK提供了ScheduledThreadPoolExecutor类来支持周期性任务的调度。
 * 在这之前的实现需要依靠Timer和TimerTask或者其它第三方工具来完成。但Timer有不少的缺陷：
 *      Timer是单线程模式；
 *      如果在执行任务期间某个TimerTask耗时较久，那么就会影响其它任务的调度；
 *      Timer的任务调度是基于绝对时间的，对系统时间敏感；
 *      Timer不会捕获执行TimerTask时所抛出的异常，由于Timer是单线程，所以一旦出现异常，则线程就会终止，其他任务也得不到执行
 *
 * ScheduledThreadPoolExecutor继承ThreadPoolExecutor来重用线程池的功能，它的实现方式如下：
 *      将任务封装成ScheduledFutureTask对象，ScheduledFutureTask基于相对时间，不受系统时间的改变所影响；
 *      ScheduledFutureTask实现了java.lang.Comparable接口和java.util.concurrent.Delayed接口，所以有两个重要的方法：compareTo和getDelay。
 *      compareTo方法用于比较任务之间的优先级关系，如果距离下次执行的时间间隔较短，则优先级高；
 *      getDelay方法用于返回距离下次任务执行时间的时间间隔；
 *      ScheduledThreadPoolExecutor定义了一个DelayedWorkQueue，它是一个有序队列，会通过每个任务按照距离下次执行时间间隔的大小来排序；
 *      ScheduledFutureTask继承自FutureTask，可以通过返回Future对象来获取执行的结果。
 * Reference
 *      https://www.jianshu.com/p/925dba9f5969
 *
 */
public class ScheExecServDemo {
    public static void main(String[] args) throws InterruptedException {
        // 创建大小为5的线程池
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

        for (int i = 0; i < 3; i++) {
            Task worker = new Task("task-" + i);
            // 只执行一次
            // scheduledThreadPool.schedule(worker, 5, TimeUnit.SECONDS)
            // 周期性执行，每5秒执行一次
            scheduledThreadPool.scheduleAtFixedRate(worker, 0, 5, TimeUnit.SECONDS);
        }

        // main线程睡10s
        Thread.sleep(10000);

        System.out.println("Shutting down executor...");
        // 10s后关闭线程池
        scheduledThreadPool.shutdown();
        boolean isDone;
        // 等待线程池终止, 10s后线程池终止
        do {
            isDone = scheduledThreadPool.awaitTermination(1, TimeUnit.DAYS);
            System.out.println("awaitTermination...");
        } while (!isDone);

        System.out.println("Finished all threads");
    }
}

class Task implements Runnable {

    private final String name;

    public Task(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("name = " + name + ", startTime = " + LocalDateTime.now());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("name = " + name + ", endTime = " + LocalDateTime.now());
    }

}
